package com.feng.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feng.entity.*;
import com.feng.entity.vo.*;
import com.feng.exception.SystemException;
import com.feng.mapper.ArticleMapper;
import com.feng.mapper.TagMapper;
import com.feng.service.*;
import com.feng.utils.Const;
import com.feng.utils.HttpCodeEnum;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * (Article)表服务实现类
 *
 * @author feng
 * @since 2024-10-18 13:51:20
 */
@Service()
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    final
    AccountRoleService accountRoleService;
    final
    ArticleTagService articleTagService;
    final
    AccountArticleService accountArticleService;

    final
    CategoryService categoryService;

    final
    RedisTemplate redisTemplate;

    final
    TagMapper tagMapper;


    public ArticleServiceImpl(AccountArticleService accountArticleService, ArticleTagService articleTagService,
                              RedisTemplate redisTemplate, CategoryService categoryService, AccountRoleService accountRoleService, TagMapper tagMapper) {
        this.accountArticleService = accountArticleService;
        this.articleTagService = articleTagService;
        this.redisTemplate = redisTemplate;
        this.categoryService = categoryService;
        this.accountRoleService = accountRoleService;
        this.tagMapper = tagMapper;
    }

    @Transactional
    @Override
    public RestResponse writeArticle(ArticleWriteVo writeVo) {
        //创建文章id
        String articleId = UUID.randomUUID().toString();

        //保存文章
        Article article = new Article(articleId, writeVo.getTitle(), writeVo.getContent(), writeVo.getSummary(), writeVo.getCategoryId(),
                writeVo.getThumbnail(), writeVo.getIsTop(), writeVo.getStatus(), writeVo.getIsComment());
        save(article);

        //获取用户ID,保存用户文章关系
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String accountId = user.getAccount().getId();
        accountArticleService.save(new AccountArticle(accountId, articleId));

        //保存文章标签
        if (!writeVo.getTags().isEmpty()) {
            List<ArticleTag> articleTags = writeVo.getTags().stream().parallel()
                    .map(tag -> new ArticleTag(articleId, tag))
                    .collect(Collectors.toList());
            articleTagService.saveBatch(articleTags);
        }
        String result = writeVo.getStatus() == 0 ? "文章发布成功" : "文章已保存到草稿箱";

        redisTemplate.opsForHash().put(Const.REDIS_VIEW_COUNT_KET, articleId, 0);
        return RestResponse.success(result);
    }

    @Override
    public RestResponse articleList(Integer current, Integer size, Long categoryId) {
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Objects.nonNull(categoryId), Article::getCategoryId, categoryId)
                .eq(Article::getStatus, Const.ZERO)
                .orderByDesc(Article::getCreateTime)
                .orderByDesc(Article::getIsTop);
        Page page = new Page<>(current, size);
        page(page, wrapper);
        List<Article> articles = page.getRecords();
        articles.stream().parallel()
                .map(article -> {
                    if (Objects.nonNull(article.getCategoryId())) {
                        article.setCategoryName(categoryService.getById(article.getCategoryId()).getName());
                    }
                    Integer articleViewCount = (Integer) redisTemplate.opsForHash().get(Const.REDIS_VIEW_COUNT_KET, article.getId());
                    Long viewCount = Long.valueOf(articleViewCount);
                    article.setViewCount(viewCount);
                    return article;
                }).collect(Collectors.toList());
        List<ArticleListVo> articleList = new ArrayList<>();
        articles.forEach(article -> articleList.add(new ArticleListVo(article.getId(), article.getTitle(), article.getSummary(),
                article.getCategoryId(), article.getCategoryName(), article.getThumbnail(), article.getViewCount(),
                article.getCreateTime())));
        ArticleListPageVo articleListPageVo = new ArticleListPageVo(articleList, page.getTotal());
        return RestResponse.success(articleListPageVo);
    }

    @Override
    public RestResponse articleDetail(String id) {
        if (Objects.isNull(id)) throw new SystemException(HttpCodeEnum.Id_NO_NULl);
        Article article = getById(id);
        if (article == null) throw new SystemException(HttpCodeEnum.NO_FIND_Article);

        Integer viewCount = (Integer) redisTemplate.opsForHash().get(Const.REDIS_VIEW_COUNT_KET, article.getId());

        if (Objects.nonNull(article.getCategoryId()))
            article.setCategoryName(categoryService.getById(article.getCategoryId()).getName());

        ArticleDetailVo articleDetail = new ArticleDetailVo(article.getId(), article.getTitle(), article.getSummary(),
                article.getContent(), article.getCategoryId(), article.getCategoryName(), article.getThumbnail(),
                Long.valueOf(viewCount), article.getIsComment(), article.getCreateTime());
        return RestResponse.success(articleDetail);
    }

    @Override
    public RestResponse updateViewCount(String id) {
        redisTemplate.opsForHash().increment(Const.REDIS_VIEW_COUNT_KET, id, 1);
        return RestResponse.success();
    }

    @Override
    public RestResponse getHotArticle() {
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper();
        wrapper.eq(Article::getStatus, Const.ZERO);
        wrapper.orderByDesc(Article::getViewCount);
        Page<Article> page = new Page<>(1, 10);
        page(page, wrapper);
        List<Article> articles = page.getRecords();
        List<Article> articleList = articles.stream()
                .map(article -> {
                    Integer viewCount = (Integer) redisTemplate.opsForHash().get(Const.REDIS_VIEW_COUNT_KET, article.getId());
                    article.setViewCount(Long.valueOf(viewCount));
                    return article;
                }).collect(Collectors.toList());
        List<HotArticleVo> hotArticleList = new ArrayList<>();
        articleList.stream().forEach(article -> {
            hotArticleList.add(new HotArticleVo(article.getId(), article.getTitle(), article.getSummary(), article.getViewCount()));
        });
        return RestResponse.success(hotArticleList);
    }

    @Override
    public RestResponse myArticle(Integer current, Integer size, Long categoryId) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String accountId = user.getAccount().getId();
        LambdaQueryWrapper<AccountArticle> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AccountArticle::getAccountId, accountId);
        List<AccountArticle> accountArticles = accountArticleService.list(wrapper);
        List<Article> articleList = accountArticles.stream()
                .map(accountArticle -> getById(accountArticle.getArticleId()))
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(
                        Article::getCreateTime,
                        Comparator.nullsLast(Comparator.reverseOrder())
                ))
                .sorted(Comparator.comparing(
                        Article::getIsTop,
                        Comparator.nullsLast(Comparator.reverseOrder())
                ))
                .skip(current-1)
                .limit(size)
                .collect(Collectors.toList());
        List<Article> articles = articleList.stream()
                .map(article -> {
                    if (article.getCategoryId() != null) {
                        article.setCategoryName(categoryService.getById(article.getCategoryId()).getName());
                    }
                    Integer articleViewCount = (Integer) redisTemplate.opsForHash().get(Const.REDIS_VIEW_COUNT_KET, article.getId());
                    Long viewCount = Long.valueOf(articleViewCount);
                    article.setViewCount(viewCount);
                    return article;
                }).collect(Collectors.toList());
        List<ArticleListVo> articleListVos = new ArrayList<>();
        articles.stream().forEach(article -> {
            articleListVos.add(new ArticleListVo(article.getId(), article.getTitle(), article.getSummary(),
                    article.getCategoryId(), article.getCategoryName(), article.getThumbnail(), article.getViewCount(),
                    article.getCreateTime()));
        });
        ArticleListPageVo articleListPageVo = new ArticleListPageVo(articleListVos, Long.valueOf(articles.size()));
        return RestResponse.success(articleListPageVo);
    }

    @Override
    public RestResponse listAllArticle(ArticlePageReqVo articlePage) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        LambdaQueryWrapper<AccountRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AccountRole::getAccountId, user.getAccount().getId());
        AccountRole accountRole = accountRoleService.getOne(wrapper);
        LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
        Page page = new Page(articlePage.getCurrent(), articlePage.getSize());
        List<Article> articles;
        List<ArticlePageListRespVo> articlePageListRespVos = new ArrayList<>();
        if (accountRole.getRoleId() == 1) {
            articleWrapper.like(StringUtils.hasText(articlePage.getTitle()), Article::getTitle, articlePage.getTitle())
                    .like(StringUtils.hasText(articlePage.getSummary()), Article::getSummary, articlePage.getSummary())
                    .orderByDesc(Article::getCreateTime)
                    .orderByDesc(Article::getIsTop);
            page(page, articleWrapper);
            articles = page.getRecords();
            articles.stream()
                    .forEach(article -> articlePageListRespVos.add(new ArticlePageListRespVo(article.getId(), article.getTitle(),
                            article.getSummary(), article.getStatus().toString(), article.getCreateTime())));
            return RestResponse.success(new ArticleListPageVo(articlePageListRespVos, page.getTotal()));
        } else {
            LambdaQueryWrapper<AccountArticle> accountArticleWrapper = new LambdaQueryWrapper<>();
            accountArticleWrapper.eq(AccountArticle::getAccountId, user.getAccount().getId());
            List<AccountArticle> accountArticles = accountArticleService.list(accountArticleWrapper);
            List<String> articleIds = accountArticles.stream().map(AccountArticle::getArticleId).collect(Collectors.toList());
            articles = articleIds.stream()
                    .map(articleId -> {
                        articleWrapper
                                .eq(Article::getId, articleId)
                                .like(StringUtils.hasText(articlePage.getTitle()), Article::getTitle, articlePage.getTitle())
                                .like(StringUtils.hasText(articlePage.getSummary()), Article::getSummary, articlePage.getSummary());
                        Article article = getOne(articleWrapper);
                        articleWrapper.clear();
                        return article;
                    })
                    .sorted(Comparator.comparing(Article::getCreateTime).reversed())
                    .sorted(Comparator.comparing(Article::getIsTop).reversed())
                    .skip(articlePage.getCurrent()-1)
                    .limit(articlePage.getSize())
                    .toList();
            articles.stream()
                    .forEach(article -> articlePageListRespVos.add(new ArticlePageListRespVo(article.getId(), article.getTitle(),
                            article.getSummary(), article.getStatus().toString(), article.getCreateTime())));
            long count = articleIds.stream()
                    .map(articleId -> getById(articleId))
                    .count();
            return RestResponse.success(new ArticleListPageVo(articlePageListRespVos, count));
        }
    }

    @Override
    public RestResponse getArticleById(String id) {
        permissionsValidation(id);
        Article article = getById(id);
        if (Objects.isNull(article)) throw new SystemException(HttpCodeEnum.THIS_ARTICLE_NO);
        UpdateArticleRespVo articleVo = new UpdateArticleRespVo();
        BeanUtils.copyProperties(article, articleVo);
        LambdaQueryWrapper<ArticleTag> atWrapper = new LambdaQueryWrapper<>();
        atWrapper.eq(ArticleTag::getArticleId, id);
        List<ArticleTag> articleTags = articleTagService.list(atWrapper);
        if (articleTags.size() > 0) {
            List<Long> tagIds = articleTags.stream()
                    .map(articleTag -> articleTag.getTagId())
                    .collect(Collectors.toList());
            articleVo.setTags(tagIds);
        }
        return RestResponse.success(articleVo);
    }

    @Transactional
    @Override
    public RestResponse updateArticle(ArticleWriteVo articleWriteVo) {
        permissionsValidation(articleWriteVo.getId());
        Article article = new Article();
        BeanUtils.copyProperties(articleWriteVo, article);
        updateById(article);
        articleTagService.remove(new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getArticleId, articleWriteVo.getId()));
        if (articleWriteVo.getTags() != null && articleWriteVo.getTags().size() > 0) {
            List<ArticleTag> articleTags = articleWriteVo.getTags().stream()
                    .map(tagId -> new ArticleTag(articleWriteVo.getId(), tagId))
                    .collect(Collectors.toList());
            articleTagService.saveBatch(articleTags);
        }
        return RestResponse.success("文章更新成功");
    }

    //验证是否有权限操作某篇文章
    private void permissionsValidation(String id) {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        AccountRole accountRole = accountRoleService.getOne(
                new LambdaQueryWrapper<AccountRole>().eq(AccountRole::getAccountId, user.getAccount().getId()));
        List<AccountArticle> accountArticles = accountArticleService.list(
                new LambdaQueryWrapper<AccountArticle>().eq(AccountArticle::getAccountId, user.getAccount().getId()));
        List<String> articlesIds = accountArticles.stream().map(AccountArticle -> AccountArticle.getArticleId()).collect(Collectors.toList());
        if (accountRole.getRoleId() != 1 && !articlesIds.contains(id))
            throw new SystemException(HttpCodeEnum.ARTICLE_NO__PERMISSION);
    }

    @Override
    public RestResponse updateStatus(UpdateStatusReqVo updateStatus) {
        Article article = new Article();
        BeanUtils.copyProperties(updateStatus, article);
        updateById(article);
        return RestResponse.success();
    }

    @Transactional
    @Override
    public RestResponse deleteArticleById(String id) {
        permissionsValidation(id);
        articleTagService.remove(new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getArticleId, id));
        removeById(id);
        redisTemplate.opsForHash().delete(Const.REDIS_VIEW_COUNT_KET, id);
        return RestResponse.success();
    }

    @Transactional
    @Override
    public RestResponse deleteBatchArticle(List<String> ids) {
        LambdaQueryWrapper<ArticleTag> wrapper = new LambdaQueryWrapper<>();
        ids.forEach(id -> {
            permissionsValidation(id);
            articleTagService.remove(wrapper.eq(ArticleTag::getTagId, id));
            wrapper.clear();
        });
        removeBatchByIds(ids);
        for (String id : ids) {
            redisTemplate.opsForHash().delete(Const.REDIS_VIEW_COUNT_KET, id);
        }
        return RestResponse.success();
    }

    @Override
    public RestResponse searchArticle(Integer current, Integer size, String keyword) {
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .eq(Article::getStatus, Const.ZERO)
                .like(Article::getTitle, keyword)
                .or()
                .like(Article::getSummary, keyword)
                .orderByDesc(Article::getCreateTime)
                .orderByDesc(Article::getIsTop);
        Page page = new Page<>(current, size);
        page(page, wrapper);
        List<Article> articles = page.getRecords();
        List<Article> articleList = articles.stream()
                .map(article -> {
                    Integer viewCount = (Integer) redisTemplate.opsForHash().get(Const.REDIS_VIEW_COUNT_KET, article.getId());
                    article.setViewCount(Long.valueOf(viewCount));
                    return article;
                }).collect(Collectors.toList());
        List<ArticleListVo> articleListVos = new ArrayList<>();
        articleList.stream()
                .forEach(article -> {
            articleListVos.add(new ArticleListVo(article.getId(), article.getTitle(), article.getSummary(),
                    article.getCategoryId(), article.getCategoryName(), article.getThumbnail(), article.getViewCount(),
                    article.getCreateTime()));
        });
        ArticleListPageVo articleListPageVo = new ArticleListPageVo(articleListVos, page.getTotal());
        return RestResponse.success(articleListPageVo);
    }
}

